package com.huawei.hwclouds.vpc.demo;

import com.huaweicloud.sdk.core.auth.BasicCredentials;
import com.huaweicloud.sdk.core.auth.ICredential;
import com.huaweicloud.sdk.core.exception.ConnectionException;
import com.huaweicloud.sdk.core.exception.RequestTimeoutException;
import com.huaweicloud.sdk.core.exception.ServiceResponseException;
import com.huaweicloud.sdk.core.http.HttpConfig;
import com.huaweicloud.sdk.vpc.v2.VpcClient;
import com.huaweicloud.sdk.vpc.v2.model.AddRouteTableRoute;
import com.huaweicloud.sdk.vpc.v2.model.CreateVpcPeeringOption;
import com.huaweicloud.sdk.vpc.v2.model.CreateVpcPeeringRequest;
import com.huaweicloud.sdk.vpc.v2.model.CreateVpcPeeringRequestBody;
import com.huaweicloud.sdk.vpc.v2.model.CreateVpcPeeringResponse;
import com.huaweicloud.sdk.vpc.v2.model.CreateVpcRouteOption;
import com.huaweicloud.sdk.vpc.v2.model.CreateVpcRouteRequest;
import com.huaweicloud.sdk.vpc.v2.model.CreateVpcRouteRequestBody;
import com.huaweicloud.sdk.vpc.v2.model.CreateVpcRouteResponse;
import com.huaweicloud.sdk.vpc.v2.model.DelRouteTableRoute;
import com.huaweicloud.sdk.vpc.v2.model.DeleteVpcPeeringRequest;
import com.huaweicloud.sdk.vpc.v2.model.DeleteVpcPeeringResponse;
import com.huaweicloud.sdk.vpc.v2.model.ListRouteTablesRequest;
import com.huaweicloud.sdk.vpc.v2.model.ListRouteTablesResponse;
import com.huaweicloud.sdk.vpc.v2.model.ListSubnetsRequest;
import com.huaweicloud.sdk.vpc.v2.model.ListSubnetsResponse;
import com.huaweicloud.sdk.vpc.v2.model.ModRouteTableRoute;
import com.huaweicloud.sdk.vpc.v2.model.RouteTableRouteAction;
import com.huaweicloud.sdk.vpc.v2.model.Subnet;
import com.huaweicloud.sdk.vpc.v2.model.UpdateRouteTableReq;
import com.huaweicloud.sdk.vpc.v2.model.UpdateRouteTableRequest;
import com.huaweicloud.sdk.vpc.v2.model.UpdateRouteTableResponse;
import com.huaweicloud.sdk.vpc.v2.model.UpdateRoutetableReqBody;
import com.huaweicloud.sdk.vpc.v2.model.VpcInfo;
import com.huaweicloud.sdk.vpc.v2.region.VpcRegion;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;

/**
 * 配置VPC的路由规则
 */
public class VPCRouteDemo {
    private static final Logger logger = LoggerFactory.getLogger(VPCRouteDemo.class.getName());

    public static void main(String[] args) {
        // 输入华为云账号的AK、SK
        String ak = "{YOUR AK}";
        String sk = "{YOUR SK}";

        // 创建vpc对等连接所需的请求端vpc id和接收端vpc id
        String requestVpcId = "{request_vpc_id}";
        String acceptVpcId = "{accept_vpc_id}";

        ICredential auth = new BasicCredentials()
            .withAk(ak)
            .withSk(sk);

        HttpConfig httpConfig = HttpConfig.getDefaultHttpConfig().withIgnoreSSLVerification(true);

        // V2接口的VpcClient，region_id处填写应用区域的id，如北京四填写cn-north-4
        VpcClient clientV2 = com.huaweicloud.sdk.vpc.v2.VpcClient.newBuilder()
            .withCredential(auth)
            .withRegion(VpcRegion.valueOf("{region_id}"))
            .withHttpConfig(httpConfig)
            .build();

        VPCRouteDemo demo = new VPCRouteDemo();
        // 创建VPC对等连接
        CreateVpcPeeringResponse createVpcPeeringResponse = demo.createVpcPeering(clientV2, requestVpcId, acceptVpcId);
        if (Objects.isNull(createVpcPeeringResponse)) {
            logger.error("Failed to create VPC peering.");
            return;
        }
        String peeringId = createVpcPeeringResponse.getPeering().getId();

        // 查询VPC中子网的网段
        List<Subnet> requestSubnetList = demo.listSubnets(clientV2,requestVpcId);
        List<Subnet> acceptSubnetList = demo.listSubnets(clientV2, acceptVpcId);
        if (requestSubnetList.isEmpty() || acceptSubnetList.isEmpty()) {
            logger.error("Failed to get subnet.");
            return;
        }

        // 创建VPC路由, 类型为VPC对等连接，路由下一跳为VPC对等连接ID，路由目的地址填写对端VPC中的子网网段
        // 创建请求端VPC路由
        demo.createVpcRoute(clientV2, requestVpcId, CreateVpcRouteOption.TypeEnum.PEERING, peeringId,
            acceptSubnetList.get(0).getCidr());
        // 创建接收端VPC路由
        demo.createVpcRoute(clientV2, acceptVpcId, CreateVpcRouteOption.TypeEnum.PEERING, peeringId,
            requestSubnetList.get(0).getCidr());

        // 通过更新路由表接口添加、修改、删除VPC路由
        // 查询路由表ID
        String requestRtbId = demo.getRouteTableId(clientV2, requestSubnetList.get(0).getId());
        // 删除路由
        demo.delRoute(clientV2, requestRtbId, "peering", acceptSubnetList.get(0).getCidr(), peeringId, "");
        // 添加路由
        demo.addRoute(clientV2, requestRtbId, "peering", acceptSubnetList.get(0).getCidr(), peeringId, "peering route");
        // 修改路由
        demo.modRoute(clientV2, requestRtbId, "peering", acceptSubnetList.get(0).getCidr(), peeringId,
            "update peering route");

        // 删除对等连接
        demo.deleteVpcPeering(clientV2, peeringId);
    }

    private CreateVpcPeeringResponse createVpcPeering(VpcClient vpcClient, String requestVpcId, String acceptVpcId) {
        CreateVpcPeeringRequest createVpcPeeringRequest = new CreateVpcPeeringRequest()
            .withBody(new CreateVpcPeeringRequestBody()
                .withPeering(new CreateVpcPeeringOption()
                    .withName("peering-test")
                    .withRequestVpcInfo(new VpcInfo()
                        .withVpcId(requestVpcId))
                    .withAcceptVpcInfo(new VpcInfo()
                        .withVpcId(acceptVpcId))));
        CreateVpcPeeringResponse createVpcPeeringResponse = null;
        try {
            createVpcPeeringResponse = vpcClient.createVpcPeering(createVpcPeeringRequest);
            logger.info(createVpcPeeringResponse.toString());
        } catch (ConnectionException e) {
            logger.error(e.toString());
        } catch (RequestTimeoutException e) {
            logger.error(e.toString());
        } catch (ServiceResponseException e) {
            logger.error(e.toString());
        }
        return createVpcPeeringResponse;
    }

    private List<Subnet> listSubnets(VpcClient vpcClient, String vpcId) {
        ListSubnetsRequest listSubnetsRequest = new ListSubnetsRequest().withVpcId(vpcId).withLimit(1);
        List<Subnet> subnetList = new ArrayList<>();
        try {
            ListSubnetsResponse listSubnetsResponse = vpcClient.listSubnets(listSubnetsRequest);
            logger.info(listSubnetsResponse.toString());
            subnetList = listSubnetsResponse.getSubnets();
        } catch (ConnectionException e) {
            logger.error(e.toString());
        } catch (RequestTimeoutException e) {
            logger.error(e.toString());
        } catch (ServiceResponseException e) {
            logger.error(e.toString());
        }
        return subnetList;
    }

    private void createVpcRoute(VpcClient vpcClient, String vpcId, CreateVpcRouteOption.TypeEnum type,
        String nexthop, String destination) {
        CreateVpcRouteRequest createVpcRouteRequest = new CreateVpcRouteRequest()
            .withBody(new CreateVpcRouteRequestBody()
                .withRoute(new CreateVpcRouteOption()
                    .withVpcId(vpcId)
                    .withType(type)
                    .withNexthop(nexthop)
                    .withDestination(destination)));
        try {
            CreateVpcRouteResponse createVpcRouteResponse = vpcClient.createVpcRoute(createVpcRouteRequest);
            logger.info(createVpcRouteResponse.toString());
        } catch (ConnectionException e) {
            logger.error(e.toString());
        } catch (RequestTimeoutException e) {
            logger.error(e.toString());
        } catch (ServiceResponseException e) {
            logger.error(e.toString());
        }
    }

    private String getRouteTableId(VpcClient vpcClient, String subnetId) {
        ListRouteTablesRequest listRouteTablesRequest =
            new ListRouteTablesRequest().withSubnetId(subnetId);
        String rtbId = "";
        try {
            ListRouteTablesResponse listRouteTablesResponse = vpcClient.listRouteTables(listRouteTablesRequest);
            logger.info(listRouteTablesResponse.toString());
            if (listRouteTablesResponse.getRoutetables().isEmpty()) {
                logger.error("The current subnet does not have an associated routetable.");
                return rtbId;
            }
            rtbId = listRouteTablesResponse.getRoutetables().get(0).getId();
        } catch (ConnectionException e) {
            logger.error(e.toString());
        } catch (RequestTimeoutException e) {
            logger.error(e.toString());
        } catch (ServiceResponseException e) {
            logger.error(e.toString());
        }
        return rtbId;
    }

    private void delRoute(VpcClient vpcClient, String rtbId, String type, String destination, String nexthop,
        String description) {
        UpdateRouteTableRequest routeTableRequest = new UpdateRouteTableRequest()
            .withRoutetableId(rtbId)
            .withBody(new UpdateRoutetableReqBody()
                .withRoutetable(new UpdateRouteTableReq()
                    .withRoutes(new RouteTableRouteAction()
                        .withDel(Arrays.asList(new DelRouteTableRoute()
                            .withType(type)
                            .withDestination(destination)
                            .withNexthop(nexthop)
                            .withDescription(description))))));
        updateRouteTable(vpcClient, routeTableRequest);
    }

    private void addRoute(VpcClient vpcClient, String rtbId, String type, String destination, String nexthop,
        String description) {
        UpdateRouteTableRequest routeTableRequest = new UpdateRouteTableRequest()
            .withRoutetableId(rtbId)
            .withBody(new UpdateRoutetableReqBody()
                .withRoutetable(new UpdateRouteTableReq()
                    .withRoutes(new RouteTableRouteAction()
                        .withAdd(Arrays.asList(new AddRouteTableRoute()
                            .withType(type)
                            .withDestination(destination)
                            .withNexthop(nexthop)
                            .withDescription(description))))));
        updateRouteTable(vpcClient, routeTableRequest);
    }

    private void modRoute(VpcClient vpcClient, String rtbId, String type, String destination, String nexthop,
        String description) {
        UpdateRouteTableRequest routeTableRequest = new UpdateRouteTableRequest()
            .withRoutetableId(rtbId)
            .withBody(new UpdateRoutetableReqBody()
                .withRoutetable(new UpdateRouteTableReq()
                    .withRoutes(new RouteTableRouteAction()
                        .withMod(Arrays.asList(new ModRouteTableRoute()
                            .withType(type)
                            .withDestination(destination)
                            .withNexthop(nexthop)
                            .withDescription(description))))));
        updateRouteTable(vpcClient, routeTableRequest);
    }

    private void updateRouteTable(VpcClient vpcClient, UpdateRouteTableRequest updateRouteTableRequest) {
        try {
            UpdateRouteTableResponse updateRouteTableResponse = vpcClient.updateRouteTable(updateRouteTableRequest);
            logger.info(updateRouteTableResponse.toString());
        } catch (ConnectionException e) {
            logger.error(e.toString());
        } catch (RequestTimeoutException e) {
            logger.error(e.toString());
        } catch (ServiceResponseException e) {
            logger.error(e.toString());
        }
    }

    private void deleteVpcPeering(VpcClient vpcClient, String peeringId) {
        DeleteVpcPeeringRequest deleteVpcPeeringRequest = new DeleteVpcPeeringRequest().withPeeringId(peeringId);
        try {
            DeleteVpcPeeringResponse deleteVpcPeeringResponse = vpcClient.deleteVpcPeering(deleteVpcPeeringRequest);
            logger.info(deleteVpcPeeringResponse.toString());
        } catch (ConnectionException e) {
            logger.error(e.toString());
        } catch (RequestTimeoutException e) {
            logger.error(e.toString());
        } catch (ServiceResponseException e) {
            logger.error(e.toString());
        }
    }
}